package cn.com.infohold.smartcity.weex;

import android.content.Context;
import android.content.res.AssetManager;
import android.net.Uri;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.webkit.URLUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Properties;

import cn.com.infohold.smartcity.data.Constants;
import cn.com.infohold.smartcity.weex.update.WeexVersion;
import common.utils.FileUtils;
import library.utils.Logger;
import okhttp3.HttpUrl;

/**
 * Created by apple on 2017/1/13.
 */

public class WeexLoader {

    private final WeakReference<Context> rtxReference;

    private static WeexVersion assetWeexVersion;
    private static WeexVersion fileWeexVersion;

    private String baseUrl;
    private String path;

    private boolean debuggable;

    private OnWeexLoadListener onWeexLoadListener;

    private WeexLoader(Context context) {
        rtxReference = new WeakReference<>(context);

        if (!debuggable && assetWeexVersion == null && fileWeexVersion == null) {
            synchronized (WeexLoader.class) {
                if (assetWeexVersion == null || fileWeexVersion == null) {
                    syncStaticVersion();
                }
            }
        }
    }

    private void syncStaticVersion() {
        try {
            assetWeexVersion = getBaseAssetVersion();
            fileWeexVersion = getWeexFileVersion();
            if (assetWeexVersion != null && fileWeexVersion != null) {
                int i = fileWeexVersion.compare(assetWeexVersion);
                if (i < 0) {
                    clearWeexCache();
                } else {
                    fileWeexVersion.setAvailable(true);
                }
            }
            Logger.debug("sync static weex version \n" +
                            "asset file [%s]\t: %s \n" +
                            "cache file [%s]\t: %s",
                    String.valueOf(assetWeexVersion == null ? null : assetWeexVersion.getVersion()),
                    String.valueOf(assetWeexVersion == null ? null : assetWeexVersion.getPath()),
                    String.valueOf(fileWeexVersion == null ? null : fileWeexVersion.getVersion()),
                    String.valueOf(fileWeexVersion == null ? null : fileWeexVersion.getPath()));
        } catch (Exception e) {

        }
    }

    protected Context getContext() {
        return rtxReference != null ? rtxReference.get() : null;
    }

    /**
     * ############################### Weex  Loader Start #####################################
     */
    public static WeexLoader obtain(@NonNull Context context) {
        return new WeexLoader(context);
    }

    public void checkUrlAndPath() {
        if (URLUtil.isNetworkUrl(baseUrl) && !TextUtils.isEmpty(path) && path.startsWith(baseUrl)) {
            path = path.replaceAll(" ", "");
            try {
                path = HttpUrl.parse(path).toString().substring(baseUrl.length());
                return;
            } catch (Exception e) {
            }
            try {
                path = Uri.parse(path).toString().substring(baseUrl.length());
                return;
            } catch (Exception e) {

            }
            path = path.substring(baseUrl.length());
        }
    }

    public WeexLoader baseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
        checkUrlAndPath();
        return this;
    }

    public WeexLoader path(String path) {
        this.path = path;
        checkUrlAndPath();
        return this;
    }

    public WeexLoader debuggable(boolean debuggable) {
        this.debuggable = debuggable;
        return this;
    }

    public void listener(OnWeexLoadListener listener) {
        this.onWeexLoadListener = listener;
    }


    /**############################### Weex  Loader End  #####################################*/

    /**
     * ############################### Weex Version Start#####################################
     */

    public WeexVersion getWeexVersion() {
        if (fileWeexVersion != null && fileWeexVersion.isAvailable() && assetWeexVersion != null) {
            int compile = fileWeexVersion.compare(assetWeexVersion);
            if (compile > 0) {
                return fileWeexVersion;
            } else {
                return assetWeexVersion;
            }
        } else if (fileWeexVersion != null && fileWeexVersion.isAvailable()) {
            return fileWeexVersion;
        } else if (assetWeexVersion != null) {
            return assetWeexVersion;
        }
        return null;
    }

    public String getWeexVersionName() {
        String vWeex = null;
        WeexVersion weexVersion = getWeexVersion();
        if (weexVersion != null) {
            vWeex = weexVersion.getVersion();
        }
        return vWeex != null ? vWeex : "0.0.0";
    }


    private static void rmFileAsync(final File file) {
        if (file == null || !file.exists())
            return;
        new Thread() {
            @Override
            public void run() {
                FileUtils.deleteFile(file);
            }
        }.start();
    }

    public void clearWeexCache() {
        if (fileWeexVersion != null) {
            fileWeexVersion.setAvailable(false);
            fileWeexVersion.setVersion(null);
            if (!TextUtils.isEmpty(fileWeexVersion.getPath())) {
                Logger.simple("Weex clear file cache @ :" + fileWeexVersion.getPath());
                rmFileAsync(new File(fileWeexVersion.getPath()));
            }
        }
    }

    public Uri getLocalAvailableFileUri() {
        String asset = getAsset();
        File file = getFile();
        if (file != null && file.exists() && file.isFile() && file.canRead()) {
            return Uri.fromFile(file);
        } else if (asset != null) {
            Context context = getContext();
            if (context != null) {
                AssetManager assetManager = context.getAssets();
                try {
                    assetManager.open(asset);
                    return Uri.parse(FileUtils.ASSET_BASE + asset);
                } catch (IOException ignore) {

                }
            }
        }
        return null;
    }

    /**############################### Weex Version End #####################################*/


    /**
     * ############################### Weex Private Start#####################################
     */


    /**
     * 遍历 Weex-version 列表
     *
     * @param list
     * @return 最新版本
     */
    private static WeexVersion checkLocalVersion(String[] list) {
        if (list == null || list.length < 1)
            return null;
        try {
            WeexVersion latestVersion = null;
            for (String path : list) {
                if (path == null)
                    continue;
                try {
                    if (!path.startsWith(Constants.Weex.DIST_NAME_DEFAULT))
                        continue;
                    final WeexVersion checkVersion = new WeexVersion(path);
                    if (latestVersion == null) {
                        latestVersion = checkVersion;
                        continue;
                    }
                    int compare = checkVersion.compare(latestVersion);
                    if (compare > 0) {
                        latestVersion = checkVersion;
                    }
                } catch (Exception e) {

                }
            }
            if (latestVersion != null)
                return latestVersion;
        } catch (Exception e) {

        }
        return null;
    }

    private WeexVersion getBaseAssetVersion() {
        try {
            Context context = getContext();
            AssetManager assetManager = context.getAssets();
            String[] list = assetManager.list("");
            WeexVersion latest = checkLocalVersion(list);
            if (latest != null) {
                return latest;
            }
        } catch (IOException e) {
            Logger.debug(e);
        }
        return null;
    }

    protected File getJournal(File dir) {
        if (!dir.exists())
            dir.mkdirs();
        return new File(dir, ".journal");
    }

    public void setWeexFileVersion(String versionName) {
        if (fileWeexVersion != null) {
            try {
                File target = new File(fileWeexVersion.getPath());
                File journal = getJournal(target);
                if (!journal.exists())
                    journal.createNewFile();
                Properties properties = new Properties();
                try {
                    properties.load(new FileInputStream(journal));
                } catch (Exception e) {

                }
                properties.setProperty("VERSION_NAME", versionName);
                properties.store(new FileOutputStream(journal), null);
                syncStaticVersion();
            } catch (Exception e) {
                Logger.debug(e);
            }
        }
    }

    private WeexVersion getWeexFileVersion() {
        File cacheDir = getCacheDir(getContext());
        File target = new File(cacheDir, Constants.Weex.DIST_NAME_DEFAULT);
        File journal = getJournal(target);
        WeexVersion cacheVersion = new WeexVersion(target.getAbsolutePath());
        try {
            if (!journal.exists())
                journal.createNewFile();
            Properties properties = new Properties();
            properties.load(new FileInputStream(journal));
            String versionName = properties.getProperty("VERSION_NAME");
            cacheVersion.setVersion(versionName);
        } catch (Exception e) {
            Logger.debug(e);
        }
        return cacheVersion;
    }

    private static File getCacheDir(Context context) {
        File weexFileDir = null;
        if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
                && Environment.isExternalStorageEmulated()) {
            File cacheDir = context.getExternalCacheDir();
            if (cacheDir != null && cacheDir.getAbsolutePath().contains(context.getPackageName())) {
                File wxFile = new File(cacheDir.getParent(), "file");
                if (wxFile.exists()) {
                    if (wxFile.isDirectory() && wxFile.canRead() && wxFile.canWrite()) {
                        weexFileDir = wxFile;
                    }
                } else if (!wxFile.exists()) {
                    if (wxFile.mkdirs() && wxFile.canRead() && wxFile.canWrite()) {
                        weexFileDir = wxFile;
                    }
                }
            }
        }
        if (weexFileDir == null)
            weexFileDir = context.getFilesDir();
        if (!weexFileDir.exists())
            weexFileDir.mkdirs();
        return weexFileDir;
    }

    public String getWeexCacheFile() {
        return fileWeexVersion.getPath();
    }

    protected String getFullPath(Object obj, String mPath) {
        if (URLUtil.isNetworkUrl(mPath))
            return mPath;
        if (obj instanceof File) {
            if (mPath != null)
                return new File((File) obj, mPath).getAbsolutePath();
            else
                return ((File) obj).getAbsolutePath();
        } else {
            if (obj == null)
                return mPath;
            String base = String.valueOf(obj);
            if (TextUtils.isEmpty(mPath))
                return base;
            if (base.endsWith("/") && mPath.startsWith("/")) {
                return base + mPath.substring(1);
            } else if (base.endsWith("/") || mPath.startsWith("/")) {
                return base + mPath;
            } else {
                return base + "/" + mPath;
            }
        }
    }

    protected String getBaseUrl() {
        return baseUrl;
    }

    protected String getPath() {
        return path;
    }

    protected String getUrl() {
        return getFullPath(baseUrl, path);
    }

    protected File getFile() {
        if (fileWeexVersion != null && path != null && !URLUtil.isNetworkUrl(path))
            return new File(getFullPath(fileWeexVersion.getPath(), path));
        return null;
    }

    public boolean isFileCacheAvailable() {
        return fileWeexVersion != null && fileWeexVersion.isAvailable();
    }

    protected String getAsset() {
        if (assetWeexVersion != null && path != null && !URLUtil.isNetworkUrl(path))
            return getFullPath(assetWeexVersion.getPath(), path);
        return null;
    }

    protected boolean isDebuggable() {
        return debuggable;
    }

    public String getBundleUrl() {
        return getUrl();
    }

    public interface OnWeexLoadListener {
        void onStart();

        void onError();

        void onFinish(String jsContent);
    }

}
